package NGUYENMINHHIEU.XMLEXCERCISE;

import java.io.File;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class StudentControllers {
    /**
     * Nghiệp vụ xử lý ghi dữ liệu xuống XML files
     * @param lisField - Chứa danh sách các trường dữ liệu. 
     * Chỉ số = 0 dành cho Node Atribute Key.
     * @param lisValues - Chứa danh sách dữ liệu tương ứng. 
     * Chỉ số = 0 chứa dữ liệu cho Node Atribute Key.
     * @param rootNode - Chứa tên node gốc (gắn trực tiếp vào Documents).
     * @param elementNode - Chứa tên node con (gắn trực tiếp vào node gốc).
     * @param resource - Chứa đường dẫn resource. Ví dụ: "ABC/XML/"
     * @param fileName - Chưa tên files XML định nghĩa. Ví dụ: "Students.xml"
     * @param writeType - Kiểu ghi dữ liệu xuống files. writeType = true: Ghi mới và 
     * writeType = false: Ghi nối tiếp.     * 
     * @return true or false
     * @throws Exception
     */
    public static boolean WriteDataXML(List<Object> lisField, List<List<Object>> lisValues, String rootNode,
            String elementNode, String resource, String fileName, boolean writeType) throws Exception {
        try {
            // 1. Lấy đường dẫn tương đối
            ClassLoader objcls = Thread.currentThread().getContextClassLoader();
            URL url = objcls.getResource(resource);

            // 2. Ghép nối đường dẫn + tên files thành chuỗi hoàn chỉnh
            String path = url.getPath() + fileName;

            // 3. Khởi tạo đối tượng files với đường dẫn đã có
            File objFile = new File(path);
            File xmlFile = null;

            /*
             * TRUE: có tồn tại files FALSE: ko tồn tại files
             */
            if (objFile.exists()) {
                try {
                    xmlFile = new File(path);
                } catch (Exception e) {
                    throw e;
                }
            }

            // 4. Khởi tạo cấu trúc XML trong memorry
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

            // 5. Khai báo các đối tượng liên quan
            Document doc = null;
            Element rootElement = null;
            Node rootNodeElement = null;

            // 6. Kiểm tra TH: Ghi mới dữ liệu ra files?
            if (writeType) {
                // a. Sinh ra 1 document mới trong bộ nhớ tạm thời
                doc = docBuilder.newDocument();

                // b. Tạo ra 1 node gốc có tên tương ứng
                rootElement = doc.createElement(rootNode); // phần tử gốc

                // c. Gắn node gốc đó và đối tượng doc đã tồn tại trước đó
                doc.appendChild(rootElement);
            } else {
                // 7. TH: Ghi nối tiếp ra files
                // a. Lấy toàn bộ dữ liệu trong files XML đã tồn tại --> Gắn vào
                // đối tượng doc
                // đã khai báo trước đó --> để có toàn bộ thông tin
                doc = docBuilder.parse(xmlFile);

                // b. Lấy phần tử đầu tiên của Doc
                rootNodeElement = doc.getFirstChild();
            }

            // 8. Duyệt danh sách và gắn các node vào docs
            for (int i = 0; i < lisValues.size(); i++) {
                Element childElement = doc.createElement(elementNode);
                // a. Kiểm tra kiểu ghi dữ liệu
                if (writeType)
                    rootElement.appendChild(childElement);
                else
                    rootNodeElement.appendChild(childElement);

                /* 9. Duyệt danh sách các trường dữ liệu */
                for (int j = 0; j < lisValues.get(i).size(); j++) {
                    if (j == 0) {
                        // RollNo
                        Attr attr = doc.createAttribute("" + lisField.get(0));
                        attr.setValue("" + lisValues.get(i).get(j));
                        childElement.setAttributeNode(attr);
                    } else {
                        // ?
                        Element nodeName = doc.createElement("" + lisField.get(j));
                        nodeName.appendChild(doc.createTextNode("" + lisValues.get(i).get(j)));
                        childElement.appendChild(nodeName);
                    }
                }

            }

            // write the content into xml file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);

            StreamResult result = new StreamResult(objFile.getPath());
            // StreamResult result = new StreamResult("D:\\iMICDemo.xml");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.transform(source, result);
            // System.out.println("File saved!");

        } catch (ParserConfigurationException pce) {
            pce.printStackTrace();
        } catch (TransformerException tfe) {
            tfe.printStackTrace();
        }
        return true;
    }

    /**
     * Nghiệp vụ cập nhật thông tin Sinh Viên
     * @param lisStudent
     * @return
     * @throws Exception
     */
    public boolean UpdateDataXML(List<Object> lisField, List<Object> lisValues, String resource, String fileName,
            String attributeKey, String elementNode) throws Exception {
        try {
            // Lấy đường dẫn tương đối
            boolean bFlag = false;
            ClassLoader objcls = Thread.currentThread().getContextClassLoader();
            URL url = objcls.getResource(resource);
            String path = url.getPath() + fileName;
            File objFile = new File(path);
            File xmlFile = null;

            /*
             * TRUE: có tồn tại files FALSE: ko tồn tại files
             */
            if (objFile.exists()) {
                try {
                    xmlFile = new File(path);
                    DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
                    DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
                    Document doc = docBuilder.parse(xmlFile);

                    // Cập nhật thông tin Sinh Viên
                    NodeList nList = doc.getElementsByTagName(elementNode);
                    for (int i = 0; i < nList.getLength(); i++) {
                        // Nhận về thông tin student element bởi tag name
                        Node nChild = doc.getElementsByTagName(elementNode).item(i);
                        NamedNodeMap attr = nChild.getAttributes();
                        Node nodeAttr = attr.getNamedItem(attributeKey);

                        if (nodeAttr.getTextContent().equals("" + lisValues.get(0))) {
                            NodeList list = nChild.getChildNodes();
                            String str = nChild.getNodeName();

                            for (int j = 0; j < list.getLength(); j++) {
                                Node node = list.item(j);

                                for (int k = 0; k < lisField.size(); k++) {
                                    if (lisField.get(k).equals(node.getNodeName())) {
                                        node.setTextContent("" + lisValues.get(k));
                                    }
                                }
                            }
                        }
                    }

                    // write the content into xml file
                    TransformerFactory transformerFactory = TransformerFactory.newInstance();
                    Transformer transformer = transformerFactory.newTransformer();
                    DOMSource source = new DOMSource(doc);
                    StreamResult result = new StreamResult(xmlFile);
                    transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                    transformer.transform(source, result);

                } catch (Exception ex) {
                    throw ex;
                }
            }
        } catch (Exception e) {
            throw e;
        }
        return true;
    }

    /**
     * Nghiệp vụ kiểm tra thông tin FullName
     * @param fullName
     * @return - Họ và Tên không chứ ký tự số và tối thiểu là 03, tối đa là 25 ký tự
     */
    public boolean CheckFullName(String fullName) {
        String USERNAME_PATTERN = "^[a-zA-Z\\sàáạã_-]{3,25}$";
        Pattern pattern = Pattern.compile(USERNAME_PATTERN);
        Matcher matcher = pattern.matcher(fullName);
        return matcher.matches();
    }

    /**
     * Nghiệp vụ kiểm tra thông tin Email
     * @param email
     * @return
     */
    public boolean CheckEmail(String email) {
        String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
                + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
        Pattern pattern = Pattern.compile(EMAIL_PATTERN);
        Matcher matcher = pattern.matcher(email);
        return matcher.matches();
    }

    /**
     * Nghiệp vụ kiểm tra thông tin điện thoại
     * @param mobiles
     * @return
     */
    public boolean CheckMobiles(String mobiles) {
        String USERNAME_PATTERN = "^[0-9_-]{3,12}$";
        Pattern pattern = Pattern.compile(USERNAME_PATTERN);
        Matcher matcher = pattern.matcher(mobiles);
        return matcher.matches();
    }

    /**
     * Nghiệp vụ convert kiểu dữ liệu từ Date --> Sang String 
     * format: "yyyy-MM-dd"
     */
    public static String convertDateToString(Date date, String format) {
        SimpleDateFormat mySimpleDateFormat = new SimpleDateFormat(format);
        return mySimpleDateFormat.format(date);
    }

    /**
     * Nghiệp vụ chuyển đổi từ String --> Date trong Java
     */
    public static Date convertStringToDate(String dateStr, String format) {
        SimpleDateFormat mySimpleDateFormat = new SimpleDateFormat(format);
        try {
            return mySimpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            return null;
        }
    }

    public static boolean isNumeric(String number) {
        boolean isValid = false;
        String expression = "^[-+]?[0-9]*\\.?[0-9]+$";
        CharSequence inputStr = number;
        Pattern pattern = Pattern.compile(expression);
        Matcher matcher = pattern.matcher(inputStr);
        if (matcher.matches()) {
            isValid = true;
        }
        return isValid;
    }

    public boolean CheckIsNumber(String number) {
        String NUMBER_PATTERN = "^[0-9.-]{1,20}$";
        Pattern pattern = Pattern.compile(NUMBER_PATTERN);
        Matcher matcher = pattern.matcher(number);
        return matcher.matches();
    }

    /*
     * Regular Expression Pattern:
     * ((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20}) Description ( # Start
     * of group (?=.*\d) # must contains one digit from 0-9 (?=.*[a-z]) # must
     * contains one lowercase characters (?=.*[A-Z]) # must contains one
     * uppercase characters (?=.*[@#$%]) # must contains one special symbols in
     * the list "@#$%" . # match anything with previous condition checking
     * {6,20} # length at least 6 characters and maximum of 20 ) # End of group
     */
    public boolean CheckPassWord(String passWord) {
        String PASSWORD_PATTERN = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})";
        Pattern pattern = Pattern.compile(PASSWORD_PATTERN);
        Matcher matcher = pattern.matcher(passWord);
        return matcher.matches();
    }

    public static boolean isPhoneNumberValid(String phoneNumber) {
        boolean isValid = false;
        /*
         * Phone Number formats: (nnn)nnn-nnnn; nnnnnnnnnn; nnn-nnn-nnnn ^\\(? :
         * May start with an option "(" . (\\d{3}): Followed by 3 digits. \\)? :
         * May have an optional ")" [- ]? : May have an optional "-" after the
         * first 3 digits or after optional ) character. (\\d{3}) : Followed by
         * 3 digits. [- ]? : May have another optional "-" after numeric digits.
         * (\\d{4})$ : ends with four digits.
         * 
         * Examples: Matches following phone numbers: (123)456-7890,
         * 123-456-7890, 1234567890, (123)-456-7890
         */
        // Initialize reg ex for phone number.
        String expression = "^\\(?(\\d{3})\\)?[- ]?(\\d{3})[- ]?(\\d{4})$";
        CharSequence inputStr = phoneNumber;
        Pattern pattern = Pattern.compile(expression);
        Matcher matcher = pattern.matcher(inputStr);
        if (matcher.matches()) {
            isValid = true;
        }
        return isValid;
    }

    /*
     * UserName the matcher demo (min: 3 và max:15 character): doannv123 : true
     * doannv_imic : true doannv-imic : true ab : false ab@abc : false abc123_-
     * : false
     */
    public boolean CheckUserName(String userName) {
        String USERNAME_PATTERN = "^[a-z0-9_-]{3,15}$";
        Pattern pattern = Pattern.compile(USERNAME_PATTERN);
        Matcher matcher = pattern.matcher(userName);
        return matcher.matches();
    }

    public boolean CheckDate(String dateString) {
        String DATE_PATTERN = "(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)";
        Pattern pattern = Pattern.compile(DATE_PATTERN);
        Matcher matcher = pattern.matcher(dateString);
        if (matcher.matches()) {
            matcher.reset();
            if (matcher.find()) {
                String day = matcher.group(1);
                String month = matcher.group(2);
                int year = Integer.parseInt(matcher.group(3));

                if (day.equals("31")
                        && (month.equals("4") || month.equals("6") || month.equals("9") || month.equals("11")
                                || month.equals("04") || month.equals("06") || month.equals("09"))) {
                    return false; // only 1,3,5,7,8,10,12 has 31 days
                } else if (month.equals("2") || month.equals("02")) {
                    // leap year
                    if (year % 4 == 0) {
                        if (day.equals("30") || day.equals("31")) {
                            return false;
                        } else {
                            return true;
                        }
                    } else {
                        if (day.equals("29") || day.equals("30") || day.equals("31")) {
                            return false;
                        } else {
                            return true;
                        }
                    }
                } else {
                    return true;
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
}
